package com.google.gwt.gfx.client;

import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.gfx.client.DragEvent.Handler;
import com.google.gwt.gfx.client.GFXPanel.Tool;
import com.google.gwt.gfx.client.graphics.Rectangle;
import com.google.gwt.gfx.client.shared.Point;
import com.google.gwt.gfx.client.shared.Size;

/**
 * A type of {@link Tool} for drawing a rectangle by dragging on a
 * {@link GFXPanel}. The style of result rectangle is defined in CSS files.
 */
public class DragRectTool<B extends GFXPanel<?, ?, ?>> implements Tool<B>,
		HasDragHandlers, DragEvent.Handler {

	private B buddyRef;

	private HandlerRegistration drag;

	private DragTool<B> dragToolRef = new DragTool<B>();

	private Point pos = new Point();

	private Rectangle rect = Rectangle.get();

	private Size size = new Size();

	@Override
	public HandlerRegistration addDragHandler(Handler handler) {
		return dragToolRef.addDragHandler(handler);
	}

	/**
	 * @param buddy
	 *            {@link #buddy()}
	 */
	@Override
	public void bind(final B buddy) {
		buddyRef = buddy;

		dragToolRef.bind(buddy);
	}

	public B buddy() {
		return buddyRef;
	}

	@Override
	public void disable() {
		buddy().overlays().remove(rect);
		drag.removeHandler();
		dragToolRef.disable();
	}

	public DragTool<B> dragTool() {
		return dragToolRef;
	}

	@Override
	public void enable() {
		buddy().graphics().add(rect);
		drag = dragToolRef.addDragHandler(this);
		dragToolRef.enable();
	}

	@Override
	public void fireEvent(GwtEvent<?> event) {
		dragToolRef.fireEvent(event);
	}

	/**
	 * @return center of result of dragging as a rectangle
	 */
	public Point getCenter() {
		Point center = new Point();
		center.setLeft(pos.getLeft() + size.getWidth() / 2);
		center.setTop(pos.getTop() + size.getHeight() / 2);
		return center;
	}

	/**
	 * @return size of result of dragging as a rectangle
	 */
	public Size getSize() {
		Size aSize = new Size();
		aSize.setWidth(size.getWidth());
		aSize.setHeight(size.getHeight());
		return aSize;
	}

	public void onDrag(DragEvent<?> event) {
		switch (event.getType()) {
		case Begin:
			pos.setLeft(event.getLeft());
			pos.setTop(event.getTop());
			size.setWidth(0);
			size.setHeight(0);

			rect.style().setWidth(0);
			rect.style().setHeight(0);
			rect.style().setLeft(pos.getLeft());
			rect.style().setTop(pos.getTop());
			rect.style().setVisible(true);
			break;
		case Continue:
			int width = Math.abs(pos.getLeft() - event.getLeft());
			int height = Math.abs(pos.getTop() - event.getTop());
			size.setWidth(width);
			size.setHeight(height);

			rect.style().setWidth(width);
			rect.style().setHeight(height);
			rect.style().setLeft(Math.min(pos.getLeft(), event.getLeft()));
			rect.style().setTop(Math.min(pos.getTop(), event.getTop()));
			break;
		case End:
			size.setWidth(Math.abs(pos.getLeft() - event.getLeft()));
			size.setHeight(Math.abs(pos.getTop() - event.getTop()));
			pos.setLeft(Math.min(pos.getLeft(), event.getLeft()));
			pos.setTop(Math.min(pos.getTop(), event.getTop()));
			break;
		default:
			break;
		}
	}

}
